package com.geoboard;

import android.content.Context;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.location.LocationProvider;
import android.os.Bundle;

/**
 * Determines the device location and makes it
 * publicly available.
 * @author Gordon Meyer 02/13
 */
public class PositionManager implements LocationListener{
	
	/******************************************************************
	 * 		Static Members
	 */
	private static final int MIN_WAIT = 5 * 1000;	// wait between location checks
	private static final int MIN_DIST = 1;
	private static final int ZOOM_LEVEL = 16;
	private static final int TWO_MINUTES = 2 * 60 * 1000;
	
	/**
	 * Singleton Instance
	 */
	private static PositionManager instance = null;
	
	private static volatile Location lastKnownLocation = null;
	private static LocationManager locationManager = null;
	//private static GoogleMap map = null;
	//private static MapFragment fragMap = null;
	
	/******************************************************************
	 * 		Static Functions
	 */
	
	/**
	 * Retrieve the last, best estimate for the devices location.
	 * @return The estimate of the devices location
	 */
	public static Location getLocation(){
		return lastKnownLocation;
	}
	
	/**
	 * Sets the best estimate of the devices location.
	 * @param newLocation The new best estimate.
	 */
	private static void setLocation(Location newLocation){
		if(newLocation != null){
			if(lastKnownLocation == null){
				/*if(map == null) map = fragMap.getMap();
				if(map != null){					
					CameraUpdate cu = CameraUpdateFactory.newLatLngZoom(
								new LatLng(newLocation.getLatitude(), newLocation.getLongitude()), ZOOM_LEVEL);
					map.moveCamera(cu);
				}*/
			}
			lastKnownLocation = newLocation;
		}
	}
	
	/**
	 * Retrieve the last, best estimate for the devices location.
	 * @return The estimate of the devices latitude and longitude.
	 */
	/*public static LatLng getLatLng(){
		LatLng r = null;
		
		if(lastKnownLocation != null){
			r = new LatLng(lastKnownLocation.getLatitude(), lastKnownLocation.getLongitude());
		}
		
		return r;
	}*/
	
	/**
	 * Compares a new location to an old location.
	 * @param newLocation 
	 * @param oldLocation
	 * @return True if newLocation is better than oldLocation.  False otherwise.
	 */
	private static boolean isLocationBetter(Location newLocation, Location oldLocation){
		if(newLocation == null) return false;
		if(oldLocation == null) return true;
		
		long timeDelta = newLocation.getTime() - oldLocation.getTime();
		
		if(timeDelta > TWO_MINUTES) return true;
		if(timeDelta < -TWO_MINUTES) return false;
		
		float accuracyDelta = newLocation.getAccuracy() - oldLocation.getAccuracy();
		
		if(accuracyDelta < 0) return true;
		
		
		return false;
	}
	
	/****************************************************
	 * 		Static Event Handlers
	 */
	
	/**
	 * Android onStart event.  Called from MainActivity
	 */
	public static void onStart(){
		/*if(fragMap == null){
			fragMap = (MapFragment)Globals.getMainActivity().getFragmentManager().
					findFragmentById(R.id.map);
		}
		map = fragMap.getMap();*/
		
		if(instance == null){
			instance = new PositionManager();
		}
	}
	
	/**
	 * Android onResume event.  Called from MainActivity
	 */
	public static void onResume(){
		if(instance != null && locationManager != null){			
			locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,MIN_WAIT, MIN_DIST, instance);
			if(locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)){
				locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,MIN_WAIT, MIN_DIST, instance);
			}
		}
	}
	
	/**
	 * Android onPause event.  Called from MainActivity
	 */
	public static void onPause(){
		if(instance != null && locationManager != null){
			locationManager.removeUpdates(instance);
		}
	}	
	
	/******************************************************************
	 * 		Constructors
	 */
	
	public PositionManager(){
		locationManager = (LocationManager)Globals.getContext().getSystemService(Context.LOCATION_SERVICE);

        Location netLoc = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);        
        Location gpsLoc = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
        
        if(isLocationBetter(gpsLoc, netLoc)){
        	setLocation(gpsLoc);
        } else {
        	setLocation(netLoc);
        }        
	}
	
	/******************************************************************
	 * 		Event Handlers
	 */
	
	/**
	 * Triggered when a new location is discovered by a provider.
	 */
	@Override
	public void onLocationChanged(Location location) {
		/*Toast.makeText(Globals.getContext(), "Location Changed",
				Toast.LENGTH_SHORT).show();*/
		
		if(isLocationBetter(location, lastKnownLocation)){
			setLocation(location);
		}
	}

	/**
	 * Triggered when a provider is disabled.
	 */
	@Override
	public void onProviderDisabled(String provider) {
		/*Toast.makeText(Globals.getContext(), "Disabled provider " + provider,
		        Toast.LENGTH_SHORT).show();*/
		if(provider.equals(LocationManager.GPS_PROVIDER)){
			locationManager.removeUpdates(instance);
			locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,MIN_WAIT, MIN_DIST, instance);
		}
		
	}

	/**
	 * Triggered when a provider is enabled.
	 */
	@Override
	public void onProviderEnabled(String provider) {
		/*Toast.makeText(Globals.getContext(), "Enabled new provider " + provider,
		        Toast.LENGTH_SHORT).show();*/
		if(provider.equals(LocationManager.GPS_PROVIDER)){
			locationManager.removeUpdates(instance);
			locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,MIN_WAIT, MIN_DIST, instance);
			locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,MIN_WAIT, MIN_DIST, instance);
		}
	}

	/**
	 * Triggered when a provider's status is changed.
	 */
	@Override
	public void onStatusChanged(String provider, int status, Bundle extras) {
		
		if(status == LocationProvider.OUT_OF_SERVICE){
			locationManager.removeUpdates(instance);
			if(provider.equals(LocationManager.GPS_PROVIDER)){			
				if(locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)){
					locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,MIN_WAIT, MIN_DIST, instance);
				}
			} else if(provider.equals(LocationManager.NETWORK_PROVIDER)){
				if(locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)){
					locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,MIN_WAIT, MIN_DIST, instance);
				}
			}
		} else if(status == LocationProvider.AVAILABLE){
			locationManager.removeUpdates(instance);
			if(locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)){
				locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,MIN_WAIT, MIN_DIST, instance);
			}
			if(locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)){
				locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,MIN_WAIT, MIN_DIST, instance);
			}
		}		
	}
}
